#include <iostream>
#include <fstream>
#include <chrono>
#include <hiredis.h>
#include <toml.hpp>

class Redux {
  private:
    std::string_view _address;
    int _port;
    redisContext *_rc;
    
  public:
    std::string errMsg;

    Redux(std::string_view address, int port) {
      _address = address;
      _port = port;
      _rc = nullptr;
    }

    bool create() {
      _rc = redisConnect(_address.data(), _port);
      
      if (_rc != nullptr)
        return true;
      
      errMsg = "create redisContext nullptr";
      return false;
    }

    bool setValue(std::string_view key, std::string_view value) {
      if (_rc == nullptr) {
        errMsg = "redisContext nullptr";
        return false;
      }

      const char *argv[] = {"set", key.data(), value.data()};
      std::size_t lens[] = {sizeof("set") - 1, key.size(), value.size()};
      int argc = std::size(lens);

      auto *p = redisCommandArgv(_rc, argc, argv, lens);
      auto *r = reinterpret_cast<redisReply *>(p);

      if (r == nullptr) {
        errMsg = "parse redis command result nullptr";
        return false;
      }

      if (r->type == REDIS_REPLY_NIL || r->type == REDIS_REPLY_ERROR) {
        freeReplyObject(r);
        errMsg = "redis command err";
        return false;
      }

      freeReplyObject(r);
      return true;
    }
};

int main(int argc, char const *argv[]) {
  std::string config_path;
  if (argc == 2) {
    config_path = argv[1];
  } else {
    config_path = "config.toml";
  }

  std::fprintf(stdout, "use config --- %s\n", config_path.data());

  auto config = toml::parse_file(config_path);
  std::string_view address = config["Redis"]["Address"].value_or("127.0.0.1");
  int port = config["Redis"]["Port"].value_or(6379);

  std::fprintf(stdout, "redis address -- %s:%d\n", address.data(), port);

  Redux redux(address, port);
  if (!redux.create()) {
    std::fprintf(stderr, "%s\n", redux.errMsg.data());
    return 128;
  }

  std::string_view key = "sura";
  std::string_view value = "hello, world!";
  auto now = std::chrono::steady_clock::now();

  if (!redux.setValue(key, value)) {
    std::fprintf(stderr, "%s\n", redux.errMsg.data());
    return 128;
  }

  auto diff = std::chrono::duration_cast<std::chrono::nanoseconds>(
            std::chrono::steady_clock::now() - now)
            .count();
  std::fprintf(stdout, "set value to redis complete -- paid: %dns\n", diff);
  return 0;
}
